package city.spring.configure.security.handler;

import city.spring.modules.log.entity.OperationLogEntity;
import city.spring.modules.log.mq.OperationLogNoticeTopic;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义注销处理程序
 *
 * @author HouKunLin
 * @date 2019/12/4 0004 10:01
 */
@Component
public class CustomLogoutHandler implements LogoutHandler {
    private final static Logger logger = LoggerFactory.getLogger(CustomLogoutHandler.class);
    private final String bearer = OAuth2AccessToken.BEARER_TYPE.toLowerCase();
    private final int bearerLen = OAuth2AccessToken.BEARER_TYPE.length();
    /**
     * 操作日志
     */
    private final OperationLogNoticeTopic operationLogNoticeTopic;
    private final TokenStore tokenStore;

    public CustomLogoutHandler(OperationLogNoticeTopic operationLogNoticeTopic, TokenStore tokenStore) {
        this.operationLogNoticeTopic = operationLogNoticeTopic;
        this.tokenStore = tokenStore;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        logger.info("自定义注销处理程序:{}", authentication);
        String accessToken = getAccessToken(request);
        if (accessToken == null) {
            return;
        }
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
        if (oAuth2AccessToken == null) {
            return;
        }
        OAuth2RefreshToken refreshToken = oAuth2AccessToken.getRefreshToken();
        if (refreshToken != null) {
            tokenStore.removeRefreshToken(refreshToken);
        }
        tokenStore.removeAccessToken(oAuth2AccessToken);

        OperationLogEntity entity = new OperationLogEntity();
        // 记录登录日志
        Object userName = oAuth2AccessToken.getAdditionalInformation().get("user_name");
        if (userName != null) {
            entity.setUserId(userName.toString());
        }
        entity.setType("登录");
        entity.setText("退出登录");
        operationLogNoticeTopic.output().send(MessageBuilder.withPayload(entity).build());
    }

    private String getAccessToken(HttpServletRequest request) {
        String accessToken = request.getHeader(HttpHeaders.AUTHORIZATION);
        // 判断来源请求是否包含oauth2授权信息,这里授权信息来源可能是头部的Authorization值以Bearer开头,或者是请求参数中包含access_token参数,满足其中一个则匹配成功
        if (accessToken != null && accessToken.toLowerCase().startsWith(bearer)) {
            return accessToken.substring(bearerLen).trim();
        }
        accessToken = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
        if (StringUtils.isNotBlank(accessToken)) {
            return accessToken;
        }
        return null;
    }

    @PostConstruct
    public void postConstruct() {
        logger.debug("自定义注销处理程序: {}", this);
    }
}
